using System;
using System.Collections.Generic;
using System.Text;
using Attach;
using Xunit;

namespace AttachTest
{
    public class AttachFrameworkUsage
    {
        private MyStubs stub;
        private Dependency dependency;

        public AttachFrameworkUsage()
        {
            stub = new MyStubs();
            dependency = stub.CreateObject<Dependency>();
        }

        [Fact]
        public void CanAttachMethod()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method1, Return.Nothing);

            // Act
            dependency.Method1();

            // Assert
            Assert.Equal(1, r.CallCount);
        }

        [Fact]
        public void CanAttachMethodWithReturnValue()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method3, Return.Value("returnvalue"));

            // Act
            string result = dependency.Method3(1);

            // Assert
            Assert.Equal("returnvalue", result);
        }

        [Fact]
        public void VerifyAttachedMethodRecordsParameters()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method2, Return.Nothing);

            // Act
            dependency.Method2("Param1");

            // Assert
            Assert.Equal("Param1", r.Parameters[0]);
        }

        [Fact]
        public void VerifyAttachedMethodSetsWasCalledOnRecording()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method1, Return.Nothing);

            // Act
            dependency.Method1();

            // Assert
            Assert.True(r.WasCalled);
        }

        [Fact]
        public void VerifyAttachedMethodRecordsCorrectCallCount()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method1, Return.Nothing);

            // Act
            dependency.Method1();
            dependency.Method1();
            dependency.Method1();

            // Assert
            Assert.Equal(3, r.CallCount);
        }

        [Fact]
        public void VerifyAttachedMethodRecordsCallHistory()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method2, Return.Nothing);

            // Act
            dependency.Method2("Param1");
            dependency.Method2("Param2");
            dependency.Method2("Param3");

            // Assert
            Assert.Equal("Param1", r.History[0].Parameters[0]);
            Assert.Equal("Param2", r.History[1].Parameters[0]);
            Assert.Equal("Param3", r.History[2].Parameters[0]);
        }

        [Fact]
        public void VerifyAttachingMultipleReturnResultsToMethod()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method3, Return.MultipleValues("returnvalue1", "returnvalue2"));

            // Act
            string result1 = dependency.Method3(1);
            string result2 = dependency.Method3(1);

            // Assert
            Assert.Equal("returnvalue1", result1);
            Assert.Equal("returnvalue2", result2);
        }

        [Fact]
        public void VerifyAttachingExceptionToMethodResultsInExceptionThrown()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method1, Return.Exception(new Exception("error")));

            // Act
            Exception result = Record.Exception(delegate { dependency.Method1(); });

            Assert.IsType(typeof(Exception), result);
            Assert.Equal("error", result.Message);
        }

        [Fact]
        public void VerifyAttachingWithDelegate()
        {
            // Arrange
            Results r = stub.Attach(dependency.Method3, Return.DelegateResult(
                delegate(object[] parameters) { return parameters[0] + "1234"; }
            ));

            // Act
            string result = dependency.Method3(111);

            // Assert
            Assert.Equal("1111234", result);
            Assert.Equal(1, r.CallCount);
            Assert.Equal(111, r.Parameters[0]);
        }
    }

    public class Dependency
    {
        public virtual void Method1() { }
        public virtual void Method2(string value) { }
        public virtual string Method3(int value) { return value.ToString(); }
    }
}
